﻿/*
-----------------------------------------------------------------------------
This source file is part of Cell Cloud.

Copyright (c) 2009-2012 Cell Cloud Team - cellcloudproject@gmail.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/

#ifndef CellLogger_h
#define CellLogger_h

#include "CellPrerequisites.h"
#include "CellString.h"

namespace cell
{
/** \addtogroup Core
 *  @{
 */
/** \addtogroup General
 *  @{
 */

// 控制日志输出的门限，计算方式如下：
// LogMessageLevel + LoggingLevel > CELL_LOG_THRESHOLD = 记录消息
#define CELL_LOG_THRESHOLD 4

/** 日志细节等级。
*/
enum LoggingLevel
{
	LL_LOW = 1,
	LL_NORMAL = 2,
	LL_HIGH = 3
};

/** 日志记录的消息价值。
*/
enum LogMessageLevel
{
	LML_TRIVIAL = 1,
	LML_NORMAL = 2,
	LML_CRITICAL = 3
};


/** 日志监听器类。
@author
	Jiangwei Xu
@remarks
	纯虚类，注册到 Logger 内监听消息写入日志的事件。
*/
class _CellExport LogListener
{
public:
	virtual ~LogListener() {};

	/**
	@remarks
		日志收到消息后调用该方法。
	@param
		message 需要写入日志的消息。
	@param
		lml 消息的日志等级。
	@param
		maskDebug 是否将日志打印到控制台。
	@param
		logName 处理该条消息的日志名称。
	*/
	virtual void messageLogged(const String& message, LogMessageLevel lml, bool maskDebug, const String& logName) = 0;
};


/**
@remarks
	写入日志数据到文件的日志类。
@note
	<br />客户程序不能直接使用该类，应当通过 LoggerManager 类使用。
*/
class _CellExport Logger : public LoggerAlloc
{
public:
	class Stream;

	CELL_AUTO_MUTEX // 设置为 public 允许外部程序加锁

	/**
	@remarks
		构造函数，由 LoggerManager 调用。
	*/
	Logger(const String& name, bool debugOutput = true, bool suppressTerminalOutput = true);

	/**
	@remarks
		析构函数。
	*/
	~Logger(void);

public:

	/// 返回日志名称。
	const String& getName() const { return mLogName; }
	/// 返回该日志是否启用了 Debug 输出。
	bool isDebugOutputEnabled() const { return mDebugOut; }
	/// 返回是否抑制终端输出。
	bool isTerminalOutputSuppressed() const { return mSuppressTerminal; }
	/// 返回该日志是否打印时间戳。
	bool isTimeStampEnabled() const { return mTimeStamp; }

	/** 记录消息到日志文件（默认日志名是<code>cell.log</code>）
	*/
	void log(const String& message, LogMessageLevel lml = LML_NORMAL, bool maskDebug = false);

	/** 返回日志使用的数据流。
	*/
	Stream stream(LogMessageLevel lml = LML_NORMAL, bool maskDebug = false);

	/**
	@remarks
		启用或禁用日志输出到 Debugger 。
	*/
	void setDebugOutputEnabled(bool debugOutput);

	/**
	@remarks
		设置日志的细节等级。
	*/
	void setLogDetail(LoggingLevel ll);

	/**
	@remarks
		启用或禁用时间戳。
	*/
	void setTimeStampEnabled(bool timeStamp);

	/** 返回日志的细节等级。
		*/
	LoggingLevel getLogDetail() const { return mLogLevel; }

	/**
	@remarks
		注册一个监听器。
	@param
		listener 监听器对象。
	*/
	void addListener(LogListener* listener);

	/**
	@remarks
		注销一个监听器。
	@param
		listener 监听器对象。
	*/
	void removeListener(LogListener* listener);

protected:
	std::ofstream mfpLog;
	FILE* mFileLog;
	LoggingLevel mLogLevel;
	bool mDebugOut;
	bool mSuppressTerminal;
	bool mTimeStamp;
	String mLogName;

	typedef vector<LogListener*>::type mtLogListener;
	mtLogListener mListeners;

public:
	/** 日志内部使用的数据流对象。
	@note
		Stream 非线程安全。多线程访问时需要对 Logger 进行线程安全管理。
	*/
	class Stream
	{
	public:
		/// Simple type to indicate a flush of the stream to the log
		struct Flush {};

		Stream(Logger* target, LogMessageLevel lml, bool maskDebug)
			:mTarget(target), mLevel(lml), mMaskDebug(maskDebug)
		{
		}
		// copy constructor
		Stream(const Stream& rhs) 
			: mTarget(rhs.mTarget), mLevel(rhs.mLevel),	mMaskDebug(rhs.mMaskDebug)
		{
			// explicit copy of stream required, gcc doesn't like implicit
			mCache.str(rhs.mCache.str());
		} 
		~Stream()
		{
			// flush on destroy
			if (mCache.tellp() > 0)
			{
				mTarget->log(mCache.str(), mLevel, mMaskDebug);
			}
		}

		template <typename T>
		Stream& operator<< (const T& v)
		{
			mCache << v;
			return *this;
		}

		Stream& operator<< (const Flush& v)
		{
			(void)v;
			mTarget->log(mCache.str(), mLevel, mMaskDebug);
			mCache.str(StringUtil::BLANK);
			return *this;
		}

	protected:
		Logger* mTarget;
		LogMessageLevel mLevel;
		bool mMaskDebug;
		typedef StringUtil::StrStreamType BaseStream;
		BaseStream mCache;
	};
};

/** @} */
/** @} */
} // end namespace cell

#endif // CellLogger_h
